package queteapuesto.tests.model;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Date;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.naming.NamingException;
import javax.naming.spi.NamingManager;

import org.openmdx.base.jmi1.Authority;
import org.openmdx.base.jmi1.Provider;
import org.openmdx.kernel.lightweight.naming.NonManagedInitialContextFactoryBuilder;
import org.openmdx.kernel.log.LoggerFactory;

import queteapuesto.model.cci2.Bet;
import queteapuesto.model.cci2.BetQuery;
import queteapuesto.model.cci2.CreditQuery;
import queteapuesto.model.cci2.GameEventComponent;
import queteapuesto.model.cci2.GameEventComponentQuery;
import queteapuesto.model.cci2.GameHasGameEvents.GameEvent;
import queteapuesto.model.cci2.GameQuery;
import queteapuesto.model.cci2.UserQuery;
import queteapuesto.model.jmi1.BetServices;
import queteapuesto.model.jmi1.BetServicesDoBetParams;
import queteapuesto.model.jmi1.BetServicesDoBetResult;
import queteapuesto.model.jmi1.Game;
import queteapuesto.model.jmi1.ModelPackage;

import junit.framework.TestCase;

public class BetServicesTestCase extends TestCase {

	private PersistenceManager pm;
	private static String providerName = "BetServicesProvider";
	private static String segmentName = "BetServicesSegment";
	private static String dbName = "betservices";
	private static String dbUser = "pguser";
	private static String dbPassw = "clemintulo";
	
	public BetServicesTestCase() throws NamingException {
		if( ! NamingManager.hasInitialContextFactoryBuilder())
			NonManagedInitialContextFactoryBuilder.install(
					Collections.singletonMap(
							"org.openmdx.comp.env.jdbc.queteapuesto",
							"jdbc:postgresql:\\/\\/127.0.0.1:5432\\/" + dbName + 
							"?user=" + dbUser + 
							"&password=" + dbPassw + 
							"&driver=org.postgresql.Driver"
						)
			);
	}
	
	public void testCreateBetServices() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		if(betServices != null) {
			pm.currentTransaction().begin();
			betServices.refDelete();
			pm.currentTransaction().commit();
		}
		
		pm.currentTransaction().begin();
		betServices = pm.newInstance(BetServices.class);
		provider.addSegment(false, segmentName, betServices);
		pm.currentTransaction().commit();
	}
	
	protected void setUp() throws Exception {
		this.pm = JDOHelper.getPersistenceManagerFactory("EntityManagerFactory")
		.getPersistenceManager("junit", null);

	}
	
	protected void tearDown() throws Exception {
		this.pm.close();
	}
	
	public void testCreateUser() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);
		
		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		
		pm.currentTransaction().begin();
		queteapuesto.model.jmi1.User anUser = modelPackage.getUser().createUser();
		queteapuesto.model.jmi1.Credit credit = modelPackage.getCredit().createCredit();
		credit.setAmount(new BigDecimal(50000.00));
		anUser.addCredit(credit);
		anUser.setName("Seba");
		anUser.setAge((short)31);
		betServices.addUser(anUser);
		pm.currentTransaction().commit();
	}
	
	public void testGetUserCredit() throws Exception {
		assertEquals(new BigDecimal(50000).doubleValue(), getCreditForUser("Seba"));
	}
	
	private double getCreditForUser(String username) {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);
		
		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		UserQuery userQuery = modelPackage.createUserQuery();
		userQuery.name().equalTo(username);
		List<queteapuesto.model.jmi1.User> users = betServices.getUser(userQuery);
		assertEquals(1, users.size());
		
		queteapuesto.model.jmi1.User anUser = users.get(0);
		
		return anUser.getCredit().iterator().next().getAmount().doubleValue();
	}
	
	public void testCreateGame() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);

		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		
		pm.currentTransaction().begin();
		
		queteapuesto.model.jmi1.Game aGame = modelPackage.getGame().createGame();
		queteapuesto.model.jmi1.ExclusiveGameEvent aExclEvt = modelPackage.getExclusiveGameEvent().createExclusiveGameEvent();
		
		Calendar aCalendar = Calendar.getInstance();
		aCalendar.set(2012, 2, 27, 20, 0);
		aGame.setDate(aCalendar.getTimeInMillis());
		
		queteapuesto.model.jmi1.GameEventComponent ganaBocaEvtComp = modelPackage.getGameEventComponent().createGameEventComponent();
		ganaBocaEvtComp.setDescription("Gana Boca");
		ganaBocaEvtComp.setExpires(aGame.getDate());
		ganaBocaEvtComp.setPay(new BigDecimal(20.0));
		aExclEvt.addGameEventComponent(ganaBocaEvtComp);
		
		queteapuesto.model.jmi1.GameEventComponent empateEvtComp = modelPackage.getGameEventComponent().createGameEventComponent();
		empateEvtComp.setDescription("Empate");
		empateEvtComp.setExpires(aGame.getDate());
		empateEvtComp.setPay(new BigDecimal(30.0));

		aExclEvt.addGameEventComponent(empateEvtComp);
		
		queteapuesto.model.jmi1.GameEventComponent ganaRacingEvtComp = modelPackage.getGameEventComponent().createGameEventComponent();
		ganaRacingEvtComp.setDescription("Gana Racing");
		ganaRacingEvtComp.setExpires(aGame.getDate());
		ganaRacingEvtComp.setPay(new BigDecimal(40.0));
		
		aExclEvt.addGameEventComponent(ganaRacingEvtComp);
		
		aGame.addGameEvent(aExclEvt);
		
		betServices.addGame(aGame);
		
		pm.currentTransaction().commit();
	}
	
	public void clearGames() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);

		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		
		pm.currentTransaction().begin();
		
		GameQuery gameQuery = modelPackage.createGameQuery();
		List<queteapuesto.model.jmi1.Game> games = betServices.getGame(gameQuery);
		games.clear();
		
		pm.currentTransaction().commit();
	}
		
	public void testDoBet() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);

		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		GameQuery gameQuery = modelPackage.createGameQuery();
		List<queteapuesto.model.jmi1.Game> games = betServices.getGame(gameQuery);
		
		assertFalse(games.isEmpty());
		
		String gameIdentity = games.get(0).getIdentity();
		
		String gameEventComponentDescription = "Gana Racing";
		BetServicesDoBetParams doBetParams = 
			modelPackage.createBetServicesDoBetParams(
					new BigDecimal(3.90), gameEventComponentDescription, gameIdentity, "Seba");
		double initialCredit = this.getCreditForUser("Seba");
		
		pm.currentTransaction().begin();
		BetServicesDoBetResult res = betServices.doBet(doBetParams);
		pm.currentTransaction().commit();
		assertNotNull(res);
		
		// verifico que se le desconto el credito.
		assertEquals(initialCredit - 3.90, this.getCreditForUser("Seba"));
	}
	
	private ModelPackage getModelPackage() {
		
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		ModelPackage modelPackage = null;
		if (betServices != null) {
			modelPackage = (ModelPackage)betServices.refImmediatePackage();
		}
		
		return modelPackage;
	}
	
	private queteapuesto.model.jmi1.User createUser(String username, short age, BigDecimal initialCredit) {
		ModelPackage modelPackage = getModelPackage();
		queteapuesto.model.jmi1.User anUser = modelPackage.getUser().createUser();
		queteapuesto.model.jmi1.Credit credit = modelPackage.getCredit().createCredit();
		credit.setAmount(initialCredit);
		anUser.addCredit(credit);
		anUser.setName(username);
		anUser.setAge(age);
		
		return anUser;
	}

	public void testCreateMultipleUsers() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		pm.currentTransaction().begin();
		queteapuesto.model.jmi1.User user1 = createUser("clemintulo", (short)40, new BigDecimal(500));
		queteapuesto.model.jmi1.User user2 = createUser("sbertran", (short)23, new BigDecimal(50));
		
		betServices.addUser(user1);
		betServices.addUser(user2);
		
		pm.currentTransaction().commit();
	}
		
	public void testDoMultipleBets() throws Exception {
		
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);

		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		GameQuery gameQuery = modelPackage.createGameQuery();
		List<queteapuesto.model.jmi1.Game> games = betServices.getGame(gameQuery);
		
		assertFalse(games.isEmpty());
		
		queteapuesto.model.jmi1.Game racingVsBoca = games.get(0);
		GameEvent<queteapuesto.model.cci2.GameEvent> gameEvents = racingVsBoca.getGameEvent();
		Iterator<queteapuesto.model.cci2.GameEvent> it_ge = gameEvents.iterator();
		while(it_ge.hasNext()) {
			queteapuesto.model.cci2.GameEventHasGameEventComponents.GameEventComponent<GameEventComponent> gec = 
				it_ge.next().getGameEventComponent();
			Iterator<GameEventComponent> it_gec = gec.iterator();
			int i=0;
			String [] users = {"Seba", "clemintulo", "sbertran"};
			BigDecimal [] bets ={new BigDecimal(3.90), new BigDecimal(8.00), new BigDecimal(23.90)};
			while(it_gec.hasNext()) {
				doBetForUser(users[i], it_gec.next().getDescription(), bets[i], betServices, racingVsBoca);
				i++;
			}
		}	
	}
	
	private void doBetForUser(String user, String eventComponentDesc, BigDecimal amount, BetServices betServices, queteapuesto.model.jmi1.Game game) {
		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		pm.currentTransaction().begin();
		double initialCredit = this.getCreditForUser(user);
		
		BetServicesDoBetParams doBetParams = 
			modelPackage.createBetServicesDoBetParams(
					amount, eventComponentDesc, game.getIdentity(), user);
		BetServicesDoBetResult res = betServices.doBet(doBetParams);
		
		pm.currentTransaction().commit();
		
		assertNotNull(res);
		// verifico que se le desconto el credito.
		assertEquals(initialCredit - amount.doubleValue(), this.getCreditForUser(user));
	}
	
	public void testGetBetsForGame() throws Exception {
		Authority auth = pm.getObjectById(Authority.class, ModelPackage.AUTHORITY_XRI);
		Provider provider = auth.getProvider(providerName);
		BetServices betServices = (BetServices) provider.getSegment(segmentName);
		assertNotNull(betServices);

		ModelPackage modelPackage = (ModelPackage)betServices.refImmediatePackage();
		GameQuery gameQuery = modelPackage.createGameQuery();
		List<queteapuesto.model.jmi1.Game> games = betServices.getGame(gameQuery);
		assertFalse(games.isEmpty());
		// Me quedo con el primer Game a modo de prueba basica.
		Game aGame = games.get(0);
		GameEvent<queteapuesto.model.cci2.GameEvent> gameEvents = aGame.getGameEvent();
		Iterator<queteapuesto.model.cci2.GameEvent> itGE = gameEvents.iterator();
		// Itero por todos los eventos del juego
		while(itGE.hasNext()) {
			queteapuesto.model.cci2.GameEvent anEvent = itGE.next();
			queteapuesto.model.cci2.GameEventHasGameEventComponents.GameEventComponent<GameEventComponent> 
				components = anEvent.getGameEventComponent();
			Iterator<GameEventComponent> itGEC = components.iterator();
			// Itero por todos los componentes de cada evento, ya
			// que es el objeto sobre el cual se realizan las apuestas.
			while(itGEC.hasNext()) {
				GameEventComponent gec = itGEC.next();
				BetQuery bq = modelPackage.createBetQuery();
				bq.gameEventComponent().equalTo(gec);
				UserQuery uq = modelPackage.createUserQuery();
				List<queteapuesto.model.jmi1.User> allUsers = betServices.getUser(uq);
				Iterator<queteapuesto.model.jmi1.User> ui = allUsers.iterator();
				while(ui.hasNext()) {
					queteapuesto.model.jmi1.User anUser = ui.next();
					List<queteapuesto.model.jmi1.Bet> anUserBet = anUser.getBet(bq);
					if( ! anUserBet.isEmpty() ) {
						System.out.println(
							"User  ["+ anUser.getName() + "]," +
							"Bet   ["+ gec.getDescription() + "], " +
							"Fecha ["+ new Date(anUserBet.get(0).getDate())+ "], " +
							"Vence ["+ new Date(gec.getExpires())+ "], " +
							"Monto ["+ anUserBet.get(0).getMount() + "]");
					}
				}
			}
		}
	}
}
